﻿// 食物链 POJ - 1182.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <stdio.h>

using namespace std;
//https://vjudge.csgrandeur.cn/problem/POJ-1182

/*
动物王国中有三类动物A,B,C，这三类动物的食物链构成了有趣的环形。A吃B， B吃C，C吃A。
现有N个动物，以1－N编号。每个动物都是A,B,C中的一种，但是我们并不知道它到底是哪一种。
有人用两种说法对这N个动物所构成的食物链关系进行描述：
第一种说法是"1 X Y"，表示X和Y是同类。
第二种说法是"2 X Y"，表示X吃Y。
此人对N个动物，用上述两种说法，一句接一句地说出K句话，这K句话有的是真的，有的是假的。
当一句话满足下列三条之一时，这句话就是假话，否则就是真话。
1） 当前的话与前面的某些真的话冲突，就是假话；
2） 当前的话中X或Y比N大，就是假话；
3） 当前的话表示X吃X，就是假话。
你的任务是根据给定的N（1 <= N <= 50,000）和K句话（0 <= K <= 100,000），输出假话的总数。
Input

第一行是两个整数N和K，以一个空格分隔。
以下K行每行是三个正整数 D，X，Y，两数之间用一个空格隔开，其中D表示说法的种类。
若D=1，则表示X和Y是同类。
若D=2，则表示X吃Y。
Output

只有一个整数，表示假话的数目。
Sample Input

100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
Sample Output

3
*/

const int N = 50010;
int f[N * 3];
int n, k;

void init() {
	for (int i = 0; i <= n*3; i++) {
		f[i] = i;
	}
}

int find(int x) {
	if (f[x] != x) f[x] = find(f[x]);
	return f[x];
}

void merge(int a, int b) {
	a = find(a); b = find(b);
	if (a < b) {	f[b] = a;
	}else {	f[a] = b;}
}

int main()
{
	//cin >> n >> k;
	scanf("%d %d",&n,&k);
	init();
	int ans = 0;
	for (int i = 0; i < k; i++) {
		int op, a, b;
		//cin >> op >> a >> b;
		scanf("%d %d %d",&op,&a,&b);
		if (a <1 || a >n || b<1 || b>n) {
			ans++; continue;
		}
		a = find(a); b = find(b);
		if (op == 1) {
			if (a == find(b + n) || a == find(b + 2 * n)) {
				ans++; continue;
			}
			merge(a, b); merge(a+n, b+n); merge(a+2*n, b+2*n);
		}
		else {
			if ( a == find(b) || a == find(b +  n) ) { ans++; continue; }
			merge(a+n, b); merge(a + 2*n, b + n); merge(a , b + 2 * n);
		}
	}
	//cout << ans << endl;
	printf("%d\n",ans);

	return 0;
}

